Data Loading and Processing

First, we’ll load our dependencies and process the crash data:

# Source utility functions (which loads libraries)
source("R/utils.R")

# Read and process crash data
crashes <- load_crash_data("data/raw/crashes/traffic_crashes.csv")
crashes_sf <- convert_to_sf(crashes)

# Read and process fatality data
crash_fatalities <- load_fatality_data("data/raw/crashes/crash_fatalities.csv")
crash_fatalities_sf <- convert_to_sf(crash_fatalities, "Longitude", "Latitude")

# Combine crashes and fatalities
combined_crashes_sf <- bind_rows(crashes_sf, crash_fatalities_sf)

District and Street Processing

Next, we’ll process the senate district data and Lake Shore Drive information:

# Load senate district shapefile and filter for lakefront districts
senate_districts <- load_district_shapefile(
  "data/raw/shapefiles/senate/senate_districts.shp",
  new_name = "senate_district"
)
Reading layer `senate_districts' from data source 
  `/Users/mmclean/dev/r/ilga_district_crashes/data/raw/shapefiles/senate/senate_districts.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 59 features and 3 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: -91.51308 ymin: 36.9703 xmax: -87.4952 ymax: 42.50848
Geodetic CRS:  NAD83
lakefront_districts <- senate_districts %>%
  filter(senate_district %in% c("6", "7", "13"))

district_6 <- senate_districts %>%
  filter(senate_district == "6")

# Spatial join crashes with lakefront districts
lakefront_crashes <- st_join(combined_crashes_sf, lakefront_districts, left = FALSE)

# Transform to meter-based CRS for accurate distance calculations
lakefront_crashes <- st_transform(lakefront_crashes, 3857)

# Keep district 6 crashes for district-specific analysis
crashes_with_district <- lakefront_crashes %>%
  filter(senate_district == "6")

# Query OSM for Lake Shore Drive
lake_shore_streets <- opq(bbox = CHICAGO_BBOX) %>%
  add_osm_feature(key = 'highway') %>%
  add_osm_feature(key = 'name', value = 'Lake Shore', value_exact = FALSE) %>%
  osmdata_sf() %>%
  pluck("osm_lines") %>%
  {rename(., street_name = name)} %>%
  st_transform(crs = 3857)

Crash Analysis

Process crashes within the Lake Shore Drive buffer:

# Create buffer around Lake Shore Drive and identify intersecting crashes
buffer <- st_buffer(lake_shore_streets, dist = BUFFER_DISTANCE_M)
intersects <- st_intersects(crashes_with_district, buffer, sparse = FALSE)

# Filter crashes within buffer and calculate counts for all lakefront districts
lakefront_intersects <- st_intersects(lakefront_crashes, buffer, sparse = FALSE)
lakefront_crashes$is_lakeshore_crash <- apply(lakefront_intersects, 1, any)
lakeshore_crashes <- lakefront_crashes %>%
  filter(is_lakeshore_crash) %>%
  mutate(
    crash_count = 1,
    fatality_count = ifelse(!is.na(FATALITY_VICTIM) & FATALITY_VICTIM != "", 1, 0),
    total_injuries = INJURIES_TOTAL,
    incap_injuries = INJURIES_INCAPACITATING
  )

# Create fatality subset
lakeshore_fatalities <- lakeshore_crashes %>%
  filter(fatality_count > 0)

# Create district-specific subsets for the time series visualizations
district6_crashes <- lakeshore_crashes %>%
  filter(senate_district == "6")

Crash Impact Visualizations

Fatalities Over Time

# Prepare fatalities data
fatalities_by_year <- district6_crashes %>%
  filter(fatality_count > 0) %>%
  st_drop_geometry() %>%
  mutate(year = lubridate::year(CRASH_DATE)) %>%
  group_by(year) %>%
  summarize(total_fatalities = sum(fatality_count, na.rm = TRUE))

# Get min and max years for consistent x-axis
min_year <- min(fatalities_by_year$year)
max_year <- max(fatalities_by_year$year)

# Create the fatalities visualization
ggplot(fatalities_by_year, aes(x = year, y = total_fatalities)) +
  geom_col(fill = "#9370DB", width = 0.7) +
  geom_text(
    aes(label = total_fatalities),
    vjust = -0.5,
    size = 3.5,
    family = "Arial"
  ) +
  labs(
    x = "Year",
    y = NULL,
    title = "Traffic Fatalities Along Lake Shore Drive in District 6",
    subtitle = "Annual count of traffic-related fatalities",
    caption = "Data: Chicago City Data Portal \"Traffic Crashes - Vision Zero Chicago Traffic Fatalities\" Retrieved Feb. 6, 2025
Includes crashes reported within 100ft of DuSable Lake Shore Drive"
  ) +
  scale_x_continuous(
    breaks = seq(min_year, max_year, by = 1)
  ) +
  scale_y_continuous(
    expand = expansion(mult = c(0, 0.15)),
    breaks = function(x) seq(0, ceiling(max(x)), by = 1)
  ) +
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold", size = 12, margin = margin(b = 10)),
    plot.subtitle = element_text(size = 10, color = "#666666"),
    plot.background = element_rect(fill = "#FFFCF7"),
    panel.background = element_rect(fill = "#FFFCF7"),
    panel.grid.major.y = element_line(color = "#f0f0f0", linewidth = 0.3),
    text = element_text(family = "Arial"),
    axis.line = element_line(color = "#666666"),
    axis.ticks = element_line(color = "#666666"),
    axis.title = element_text(color = "#666666"),
    axis.text = element_text(color = "#666666"),
    plot.caption = element_text(
      color = "#666666",
      size = 8,
      margin = margin(t = 10),
      hjust = 0
    ),
    plot.margin = margin(t = 15, r = 20, b = 15, l = 15, unit = "pt")
  )

Total Injuries Over Time

# Prepare injuries data
injuries_by_year <- district6_crashes %>%
  st_drop_geometry() %>%
  mutate(
    year = lubridate::year(CRASH_DATE),
    total_injuries = INJURIES_TOTAL
  ) %>%
  group_by(year) %>%
  summarize(total_injuries = sum(total_injuries, na.rm = TRUE))

# Create the injuries visualization
ggplot(injuries_by_year, aes(x = year, y = total_injuries)) +
  geom_col(fill = "#9370DB", width = 0.7) +
  geom_text(
    aes(label = total_injuries),
    vjust = -0.5,
    size = 3.5,
    family = "Arial"
  ) +
  labs(
    x = "Year",
    y = NULL,
    title = "Injuries From Crashes Along DuSable Lake Shore Drive in District 6",
    subtitle = "Total persons sustaining fatal, incapacitating, non-incapacitating, and possible injuries",
    caption = "Data: Chicago City Data Portal \"Traffic Crashes\" Retrieved Feb. 6, 2025
Includes crashes reported within 100ft of DuSable Lake Shore Drive"
  ) +
  scale_x_continuous(
    breaks = seq(min_year, max_year, by = 1)
  ) +
  scale_y_continuous(
    expand = expansion(mult = c(0, 0.15)),
    breaks = function(x) seq(0, ceiling(max(x)), by = 50)
  ) +
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold", size = 12, margin = margin(b = 10)),
    plot.subtitle = element_text(size = 10, color = "#666666"),
    plot.background = element_rect(fill = "#FFFCF7"),
    panel.background = element_rect(fill = "#FFFCF7"),
    panel.grid.major.y = element_line(color = "#f0f0f0", linewidth = 0.3),
    text = element_text(family = "Arial"),
    axis.line = element_line(color = "#666666"),
    axis.ticks = element_line(color = "#666666"),
    axis.title = element_text(color = "#666666"),
    axis.text = element_text(color = "#666666"),
    plot.caption = element_text(
      color = "#666666",
      size = 8,
      margin = margin(t = 10),
      hjust = 0
    ),
    plot.margin = margin(t = 15, r = 20, b = 15, l = 15, unit = "pt")
  )

Incapacitating Injuries Over Time

# Prepare incapacitating injuries data
incap_injuries_by_year <- district6_crashes %>%
  st_drop_geometry() %>%
  mutate(
    year = lubridate::year(CRASH_DATE),
    incap_injuries = INJURIES_INCAPACITATING
  ) %>%
  group_by(year) %>%
  summarize(total_incap_injuries = sum(incap_injuries, na.rm = TRUE))

# Create the incapacitating injuries visualization
ggplot(incap_injuries_by_year, aes(x = year, y = total_incap_injuries)) +
  geom_col(fill = "#9370DB", width = 0.7) +
  geom_text(
    aes(label = total_incap_injuries),
    vjust = -0.5,
    size = 3.5,
    family = "Arial"
  ) +
  labs(
    x = "Year",
    y = NULL,
    title = "Incapacitating Injuries Along DuSable Lake Shore Drive in District 6",
    subtitle = "Total persons sustaining incapacitating/serious injuries in a crash",
    caption = "Data: Chicago City Data Portal \"Traffic Crashes\" Retrieved Feb. 6, 2025
Includes crashes reported within 100ft of DuSable Lake Shore Drive"
  ) +
  scale_x_continuous(
    breaks = seq(min_year, max_year, by = 1)
  ) +
  scale_y_continuous(
    expand = expansion(mult = c(0, 0.15)),
    breaks = function(x) seq(0, ceiling(max(x)), by = 5)
  ) +
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold", size = 12, margin = margin(b = 10)),
    plot.subtitle = element_text(size = 10, color = "#666666"),
    plot.background = element_rect(fill = "#FFFCF7"),
    panel.background = element_rect(fill = "#FFFCF7"),
    panel.grid.major.y = element_line(color = "#f0f0f0", linewidth = 0.3),
    text = element_text(family = "Arial"),
    axis.line = element_line(color = "#666666"),
    axis.ticks = element_line(color = "#666666"),
    axis.title = element_text(color = "#666666"),
    axis.text = element_text(color = "#666666"),
    plot.caption = element_text(
      color = "#666666",
      size = 8,
      margin = margin(t = 10),
      hjust = 0
    ),
    plot.margin = margin(t = 15, r = 20, b = 15, l = 15, unit = "pt")
  )

Summary Statistics

# Calculate Lake Shore Drive statistics
lakeshore_total_crashes <- nrow(district6_crashes)
lakeshore_total_injuries <- sum(district6_crashes$INJURIES_TOTAL, na.rm = TRUE)
lakeshore_total_incap_injuries <- sum(district6_crashes$INJURIES_INCAPACITATING, na.rm = TRUE)
lakeshore_total_fatalities <- sum(district6_crashes$fatality_count, na.rm = TRUE)

# Calculate District 6 total statistics
district_total_crashes <- nrow(crashes_with_district)
district_total_injuries <- sum(crashes_with_district$INJURIES_TOTAL, na.rm = TRUE)
district_total_incap_injuries <- sum(crashes_with_district$INJURIES_INCAPACITATING, na.rm = TRUE)
district_total_fatalities <- sum(!is.na(crashes_with_district$FATALITY_VICTIM) & crashes_with_district$FATALITY_VICTIM != "", na.rm = TRUE)

# Create summary statistics dataframe
crash_stats <- tibble(
  Metric = c("Total Crashes", "Total Injuries", "Incapacitating Injuries", "Fatalities"),
  `Lake Shore Drive` = c(lakeshore_total_crashes, lakeshore_total_injuries, lakeshore_total_incap_injuries, lakeshore_total_fatalities),
  `District 6 Total` = c(district_total_crashes, district_total_injuries, district_total_incap_injuries, district_total_fatalities)
) %>%
  mutate(
    `Percentage of District Total` = sprintf("%.1f%%", (`Lake Shore Drive` / `District 6 Total`) * 100)
  )

# Create formatted table
knitr::kable(crash_stats, format = "html") %>%
  kableExtra::kable_styling(
    bootstrap_options = "striped",
    full_width = FALSE,
    position = "left"
  ) %>%
  kableExtra::row_spec(0, bold = TRUE, color = "white", background = "#4B0082") %>%
  kableExtra::column_spec(1, bold = TRUE)
Metric Lake Shore Drive District 6 Total Percentage of District Total
Total Crashes 6248 32490 19.2%
Total Injuries 1415 5452 26.0%
Incapacitating Injuries 166 584 28.4%
Fatalities 16 26 61.5%

Interactive Map Visualization

District 6 Fatality Map

Create an interactive map showing fatality locations in District 6:

# Transform geometries to WGS84 for mapping
lakeshore_fatalities <- st_transform(lakeshore_fatalities, 4326)
lake_shore_streets <- st_transform(lake_shore_streets, 4326)
buffer <- st_transform(buffer, 4326)
district_6 <- st_transform(district_6, 4326)

# Create Lake Shore Drive district 6 fatality map
district6_fatality_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  # Add district 6
  addPolygons(
    data = district_6,
    fillColor = "#9370DB",  # Medium purple
    fillOpacity = 0.3,
    color = "#4B0082",  # Indigo
    weight = 2,
    label = ~paste("Senate District", senate_district)
  ) %>%
  # Add Lake Shore Drive
  addPolylines(
    data = lake_shore_streets,
    color = "#CCCCCC",
    weight = 3
  ) %>%
  # Add the buffer area
  addPolygons(
    data = buffer,
    fillColor = "#CCCCCC",  # Light gray
    fillOpacity = 0.2,
    color = "#CCCCCC",  # Light gray
    weight = 2
  ) %>%
  # Add clustered fatality circles
  addCircleMarkers(
    data = lakeshore_fatalities,
    lng = ~st_coordinates(geometry)[, 1],
    lat = ~st_coordinates(geometry)[, 2],
    radius = 6,  # Base size for individual points
    color = "red",
    fillColor = "red",
    fillOpacity = 0.6,
    stroke = TRUE,
    weight = 1,
    popup = ~paste(
      "Date:", CRASH_DATE, "<br>",
      "Fatalities:", fatality_count, "<br>",
      "Type:", FIRST_CRASH_TYPE
    ),
    clusterOptions = markerClusterOptions(
      maxClusterRadius = 50 * 0.0003,  # Approximate conversion of 50ft to degrees
      spiderfyOnMaxZoom = TRUE,
      iconCreateFunction = JS("
        function(cluster) {
          var count = cluster.getChildCount();
          var size = Math.min(50, 20 + Math.sqrt(count) * 8);
          return new L.DivIcon({
            html: '<div style=\"background-color: rgba(255, 0, 0, 0.6); width: ' + size + 'px; height: ' + size + 'px; margin-left: -' + (size/2) + 'px; margin-top: -' + (size/2) + 'px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; border: 2px solid rgba(255, 0, 0, 0.8);\">' + count + '</div>',
            className: 'marker-cluster-custom',
            iconSize: L.point(size, size)
          });
        }
      ")
    )
  ) %>%
  setView(lng = -87.6298, lat = 41.8781, zoom = 12)

# Display the map
district6_fatality_map

Lake Shore Drive Injury Map

Create interactive maps showing injuries, incapacitating injuries, and fatalities along Lake Shore Drive:

# Transform geometries to WGS84 for mapping
lakeshore_crashes_4326 <- st_transform(lakeshore_crashes, 4326)
lakefront_districts_4326 <- st_transform(lakefront_districts, 4326)

# Create binary indicators for crashes with injuries
lakeshore_crashes_4326 <- lakeshore_crashes_4326 %>%
  mutate(
    has_injuries = ifelse(INJURIES_TOTAL > 0, 1, 0),
    has_incap_injuries = ifelse(INJURIES_INCAPACITATING > 0, 1, 0)
  )

# Create injuries heatmap with adjusted parameters
injuries_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  # Add lakefront districts outlines
  addPolygons(
    data = lakefront_districts_4326,
    fillOpacity = 0,
    color = "#4B0082",  # Indigo
    weight = 2,
    label = ~paste("Senate District", senate_district)
  ) %>%
  # Add Lake Shore Drive
  addPolylines(
    data = lake_shore_streets,
    color = "#666666",
    weight = 3
  ) %>%
  # Add injuries heatmap
  addHeatmap(
    data = lakeshore_crashes_4326 %>% filter(has_injuries == 1),
    lng = ~st_coordinates(geometry)[, 1],
    lat = ~st_coordinates(geometry)[, 2],
    intensity = ~1,  # Each crash with injuries counts as 1
    blur = 15,
    max = 0.1,
    radius = 12
  ) %>%
  setView(lng = -87.6298, lat = 41.8781, zoom = 12)

# Display the map
injuries_map

Lake Shore Drive Incapacitating Injury Map

# Create incapacitating injuries heatmap
incap_injuries_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  # Add lakefront districts outlines
  addPolygons(
    data = lakefront_districts_4326,
    fillOpacity = 0,
    color = "#4B0082",  # Indigo
    weight = 2,
    label = ~paste("Senate District", senate_district)
  ) %>%
  # Add Lake Shore Drive
  addPolylines(
    data = lake_shore_streets,
    color = "#666666",
    weight = 3
  ) %>%
  # Add incapacitating injuries heatmap
  addHeatmap(
    data = lakeshore_crashes_4326 %>% filter(has_incap_injuries == 1),
    lng = ~st_coordinates(geometry)[, 1],
    lat = ~st_coordinates(geometry)[, 2],
    intensity = ~1,  # Each crash with incapacitating injuries counts as 1
    blur = 15,
    max = 0.1,
    radius = 12
  ) %>%
  setView(lng = -87.6298, lat = 41.8781, zoom = 12)

# Display the map
incap_injuries_map
### Lake Shore Drive Fatality Map

# Create fatalities map with clusters
fatalities_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  # Add lakefront districts outlines
  addPolygons(
    data = lakefront_districts_4326,
    fillOpacity = 0,
    color = "#4B0082",  # Indigo
    weight = 2,
    label = ~paste("Senate District", senate_district)
  ) %>%
  # Add Lake Shore Drive
  addPolylines(
    data = lake_shore_streets,
    color = "#666666",
    weight = 3
  ) %>%
  # Add clustered fatality circles
  addCircleMarkers(
    data = lakeshore_fatalities,
    lng = ~st_coordinates(geometry)[, 1],
    lat = ~st_coordinates(geometry)[, 2],
    radius = 6,
    color = "red",
    fillColor = "red",
    fillOpacity = 0.6,
    stroke = TRUE,
    weight = 1,
    popup = ~paste(
      "Date:", CRASH_DATE, "<br>",
      "Fatalities:", fatality_count, "<br>",
      "Type:", FIRST_CRASH_TYPE
    ),
    clusterOptions = markerClusterOptions(
      maxClusterRadius = 50 * 0.0003,
      spiderfyOnMaxZoom = TRUE,
      iconCreateFunction = JS("
        function(cluster) {
          var count = cluster.getChildCount();
          var size = Math.min(50, 20 + Math.sqrt(count) * 8);
          return new L.DivIcon({
            html: '<div style=\"background-color: rgba(255, 0, 0, 0.6); width: ' + size + 'px; height: ' + size + 'px; margin-left: -' + (size/2) + 'px; margin-top: -' + (size/2) + 'px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; border: 2px solid rgba(255, 0, 0, 0.8);\">' + count + '</div>',
            className: 'marker-cluster-custom',
            iconSize: L.point(size, size)
          });
        }
      ")
    )
  ) %>%
  setView(lng = -87.6298, lat = 41.8781, zoom = 12)

# Display the map
fatalities_map
LS0tCnRpdGxlOiAiTGFrZSBTaG9yZSBEcml2ZSBEaXN0cmljdCA2IENyYXNoIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMjIERhdGEgTG9hZGluZyBhbmQgUHJvY2Vzc2luZwoKRmlyc3QsIHdlJ2xsIGxvYWQgb3VyIGRlcGVuZGVuY2llcyBhbmQgcHJvY2VzcyB0aGUgY3Jhc2ggZGF0YToKCmBgYHtyIGxvYWQtZGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBTb3VyY2UgdXRpbGl0eSBmdW5jdGlvbnMgKHdoaWNoIGxvYWRzIGxpYnJhcmllcykKc291cmNlKCJSL3V0aWxzLlIiKQoKIyBSZWFkIGFuZCBwcm9jZXNzIGNyYXNoIGRhdGEKY3Jhc2hlcyA8LSBsb2FkX2NyYXNoX2RhdGEoImRhdGEvcmF3L2NyYXNoZXMvdHJhZmZpY19jcmFzaGVzLmNzdiIpCmNyYXNoZXNfc2YgPC0gY29udmVydF90b19zZihjcmFzaGVzKQoKIyBSZWFkIGFuZCBwcm9jZXNzIGZhdGFsaXR5IGRhdGEKY3Jhc2hfZmF0YWxpdGllcyA8LSBsb2FkX2ZhdGFsaXR5X2RhdGEoImRhdGEvcmF3L2NyYXNoZXMvY3Jhc2hfZmF0YWxpdGllcy5jc3YiKQpjcmFzaF9mYXRhbGl0aWVzX3NmIDwtIGNvbnZlcnRfdG9fc2YoY3Jhc2hfZmF0YWxpdGllcywgIkxvbmdpdHVkZSIsICJMYXRpdHVkZSIpCgojIENvbWJpbmUgY3Jhc2hlcyBhbmQgZmF0YWxpdGllcwpjb21iaW5lZF9jcmFzaGVzX3NmIDwtIGJpbmRfcm93cyhjcmFzaGVzX3NmLCBjcmFzaF9mYXRhbGl0aWVzX3NmKQpgYGAKCiMjIERpc3RyaWN0IGFuZCBTdHJlZXQgUHJvY2Vzc2luZwoKTmV4dCwgd2UnbGwgcHJvY2VzcyB0aGUgc2VuYXRlIGRpc3RyaWN0IGRhdGEgYW5kIExha2UgU2hvcmUgRHJpdmUgaW5mb3JtYXRpb246CgpgYGB7ciBwcm9jZXNzLWRpc3RyaWN0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIExvYWQgc2VuYXRlIGRpc3RyaWN0IHNoYXBlZmlsZSBhbmQgZmlsdGVyIGZvciBsYWtlZnJvbnQgZGlzdHJpY3RzCnNlbmF0ZV9kaXN0cmljdHMgPC0gbG9hZF9kaXN0cmljdF9zaGFwZWZpbGUoCiAgImRhdGEvcmF3L3NoYXBlZmlsZXMvc2VuYXRlL3NlbmF0ZV9kaXN0cmljdHMuc2hwIiwKICBuZXdfbmFtZSA9ICJzZW5hdGVfZGlzdHJpY3QiCikKbGFrZWZyb250X2Rpc3RyaWN0cyA8LSBzZW5hdGVfZGlzdHJpY3RzICU+JQogIGZpbHRlcihzZW5hdGVfZGlzdHJpY3QgJWluJSBjKCI2IiwgIjciLCAiMTMiKSkKCmRpc3RyaWN0XzYgPC0gc2VuYXRlX2Rpc3RyaWN0cyAlPiUKICBmaWx0ZXIoc2VuYXRlX2Rpc3RyaWN0ID09ICI2IikKCiMgU3BhdGlhbCBqb2luIGNyYXNoZXMgd2l0aCBsYWtlZnJvbnQgZGlzdHJpY3RzCmxha2Vmcm9udF9jcmFzaGVzIDwtIHN0X2pvaW4oY29tYmluZWRfY3Jhc2hlc19zZiwgbGFrZWZyb250X2Rpc3RyaWN0cywgbGVmdCA9IEZBTFNFKQoKIyBUcmFuc2Zvcm0gdG8gbWV0ZXItYmFzZWQgQ1JTIGZvciBhY2N1cmF0ZSBkaXN0YW5jZSBjYWxjdWxhdGlvbnMKbGFrZWZyb250X2NyYXNoZXMgPC0gc3RfdHJhbnNmb3JtKGxha2Vmcm9udF9jcmFzaGVzLCAzODU3KQoKIyBLZWVwIGRpc3RyaWN0IDYgY3Jhc2hlcyBmb3IgZGlzdHJpY3Qtc3BlY2lmaWMgYW5hbHlzaXMKY3Jhc2hlc193aXRoX2Rpc3RyaWN0IDwtIGxha2Vmcm9udF9jcmFzaGVzICU+JQogIGZpbHRlcihzZW5hdGVfZGlzdHJpY3QgPT0gIjYiKQoKIyBRdWVyeSBPU00gZm9yIExha2UgU2hvcmUgRHJpdmUKbGFrZV9zaG9yZV9zdHJlZXRzIDwtIG9wcShiYm94ID0gQ0hJQ0FHT19CQk9YKSAlPiUKICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gJ2hpZ2h3YXknKSAlPiUKICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gJ25hbWUnLCB2YWx1ZSA9ICdMYWtlIFNob3JlJywgdmFsdWVfZXhhY3QgPSBGQUxTRSkgJT4lCiAgb3NtZGF0YV9zZigpICU+JQogIHBsdWNrKCJvc21fbGluZXMiKSAlPiUKICB7cmVuYW1lKC4sIHN0cmVldF9uYW1lID0gbmFtZSl9ICU+JQogIHN0X3RyYW5zZm9ybShjcnMgPSAzODU3KQpgYGAKCiMjIENyYXNoIEFuYWx5c2lzCgpQcm9jZXNzIGNyYXNoZXMgd2l0aGluIHRoZSBMYWtlIFNob3JlIERyaXZlIGJ1ZmZlcjoKCmBgYHtyIGFuYWx5emUtY3Jhc2hlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBDcmVhdGUgYnVmZmVyIGFyb3VuZCBMYWtlIFNob3JlIERyaXZlIGFuZCBpZGVudGlmeSBpbnRlcnNlY3RpbmcgY3Jhc2hlcwpidWZmZXIgPC0gc3RfYnVmZmVyKGxha2Vfc2hvcmVfc3RyZWV0cywgZGlzdCA9IEJVRkZFUl9ESVNUQU5DRV9NKQppbnRlcnNlY3RzIDwtIHN0X2ludGVyc2VjdHMoY3Jhc2hlc193aXRoX2Rpc3RyaWN0LCBidWZmZXIsIHNwYXJzZSA9IEZBTFNFKQoKIyBGaWx0ZXIgY3Jhc2hlcyB3aXRoaW4gYnVmZmVyIGFuZCBjYWxjdWxhdGUgY291bnRzIGZvciBhbGwgbGFrZWZyb250IGRpc3RyaWN0cwpsYWtlZnJvbnRfaW50ZXJzZWN0cyA8LSBzdF9pbnRlcnNlY3RzKGxha2Vmcm9udF9jcmFzaGVzLCBidWZmZXIsIHNwYXJzZSA9IEZBTFNFKQpsYWtlZnJvbnRfY3Jhc2hlcyRpc19sYWtlc2hvcmVfY3Jhc2ggPC0gYXBwbHkobGFrZWZyb250X2ludGVyc2VjdHMsIDEsIGFueSkKbGFrZXNob3JlX2NyYXNoZXMgPC0gbGFrZWZyb250X2NyYXNoZXMgJT4lCiAgZmlsdGVyKGlzX2xha2VzaG9yZV9jcmFzaCkgJT4lCiAgbXV0YXRlKAogICAgY3Jhc2hfY291bnQgPSAxLAogICAgZmF0YWxpdHlfY291bnQgPSBpZmVsc2UoIWlzLm5hKEZBVEFMSVRZX1ZJQ1RJTSkgJiBGQVRBTElUWV9WSUNUSU0gIT0gIiIsIDEsIDApLAogICAgdG90YWxfaW5qdXJpZXMgPSBJTkpVUklFU19UT1RBTCwKICAgIGluY2FwX2luanVyaWVzID0gSU5KVVJJRVNfSU5DQVBBQ0lUQVRJTkcKICApCgojIENyZWF0ZSBmYXRhbGl0eSBzdWJzZXQKbGFrZXNob3JlX2ZhdGFsaXRpZXMgPC0gbGFrZXNob3JlX2NyYXNoZXMgJT4lCiAgZmlsdGVyKGZhdGFsaXR5X2NvdW50ID4gMCkKCiMgQ3JlYXRlIGRpc3RyaWN0LXNwZWNpZmljIHN1YnNldHMgZm9yIHRoZSB0aW1lIHNlcmllcyB2aXN1YWxpemF0aW9ucwpkaXN0cmljdDZfY3Jhc2hlcyA8LSBsYWtlc2hvcmVfY3Jhc2hlcyAlPiUKICBmaWx0ZXIoc2VuYXRlX2Rpc3RyaWN0ID09ICI2IikKYGBgCgojIyBDcmFzaCBJbXBhY3QgVmlzdWFsaXphdGlvbnMKCiMjIyBGYXRhbGl0aWVzIE92ZXIgVGltZQoKYGBge3IgZmF0YWxpdGllcy1jaGFydCwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBQcmVwYXJlIGZhdGFsaXRpZXMgZGF0YQpmYXRhbGl0aWVzX2J5X3llYXIgPC0gZGlzdHJpY3Q2X2NyYXNoZXMgJT4lCiAgZmlsdGVyKGZhdGFsaXR5X2NvdW50ID4gMCkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIG11dGF0ZSh5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKENSQVNIX0RBVEUpKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpemUodG90YWxfZmF0YWxpdGllcyA9IHN1bShmYXRhbGl0eV9jb3VudCwgbmEucm0gPSBUUlVFKSkKCiMgR2V0IG1pbiBhbmQgbWF4IHllYXJzIGZvciBjb25zaXN0ZW50IHgtYXhpcwptaW5feWVhciA8LSBtaW4oZmF0YWxpdGllc19ieV95ZWFyJHllYXIpCm1heF95ZWFyIDwtIG1heChmYXRhbGl0aWVzX2J5X3llYXIkeWVhcikKCiMgQ3JlYXRlIHRoZSBmYXRhbGl0aWVzIHZpc3VhbGl6YXRpb24KZ2dwbG90KGZhdGFsaXRpZXNfYnlfeWVhciwgYWVzKHggPSB5ZWFyLCB5ID0gdG90YWxfZmF0YWxpdGllcykpICsKICBnZW9tX2NvbChmaWxsID0gIiM5MzcwREIiLCB3aWR0aCA9IDAuNykgKwogIGdlb21fdGV4dCgKICAgIGFlcyhsYWJlbCA9IHRvdGFsX2ZhdGFsaXRpZXMpLAogICAgdmp1c3QgPSAtMC41LAogICAgc2l6ZSA9IDMuNSwKICAgIGZhbWlseSA9ICJBcmlhbCIKICApICsKICBsYWJzKAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSBOVUxMLAogICAgdGl0bGUgPSAiVHJhZmZpYyBGYXRhbGl0aWVzIEFsb25nIExha2UgU2hvcmUgRHJpdmUgaW4gRGlzdHJpY3QgNiIsCiAgICBzdWJ0aXRsZSA9ICJBbm51YWwgY291bnQgb2YgdHJhZmZpYy1yZWxhdGVkIGZhdGFsaXRpZXMiLAogICAgY2FwdGlvbiA9ICJEYXRhOiBDaGljYWdvIENpdHkgRGF0YSBQb3J0YWwgXCJUcmFmZmljIENyYXNoZXMgLSBWaXNpb24gWmVybyBDaGljYWdvIFRyYWZmaWMgRmF0YWxpdGllc1wiIFJldHJpZXZlZCBGZWIuIDYsIDIwMjUKSW5jbHVkZXMgY3Jhc2hlcyByZXBvcnRlZCB3aXRoaW4gMTAwZnQgb2YgRHVTYWJsZSBMYWtlIFNob3JlIERyaXZlIgogICkgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IHNlcShtaW5feWVhciwgbWF4X3llYXIsIGJ5ID0gMSkKICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4xNSkpLAogICAgYnJlYWtzID0gZnVuY3Rpb24oeCkgc2VxKDAsIGNlaWxpbmcobWF4KHgpKSwgYnkgPSAxKQogICkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMiwgbWFyZ2luID0gbWFyZ2luKGIgPSAxMCkpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yID0gIiM2NjY2NjYiKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNGRkZDRjciKSwKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRkZGQ0Y3IiksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiI2YwZjBmMCIsIGxpbmV3aWR0aCA9IDAuMyksCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBcmlhbCIpLAogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiM2NjY2NjYiKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzY2NjY2NiIpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjNjY2NjY2IiksCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzY2NjY2NiIpLAogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KAogICAgICBjb2xvciA9ICIjNjY2NjY2IiwKICAgICAgc2l6ZSA9IDgsCiAgICAgIG1hcmdpbiA9IG1hcmdpbih0ID0gMTApLAogICAgICBoanVzdCA9IDAKICAgICksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbih0ID0gMTUsIHIgPSAyMCwgYiA9IDE1LCBsID0gMTUsIHVuaXQgPSAicHQiKQogICkKYGBgCgojIyMgVG90YWwgSW5qdXJpZXMgT3ZlciBUaW1lCgpgYGB7ciBpbmp1cmllcy1jaGFydCwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBQcmVwYXJlIGluanVyaWVzIGRhdGEKaW5qdXJpZXNfYnlfeWVhciA8LSBkaXN0cmljdDZfY3Jhc2hlcyAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKAogICAgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihDUkFTSF9EQVRFKSwKICAgIHRvdGFsX2luanVyaWVzID0gSU5KVVJJRVNfVE9UQUwKICApICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIHN1bW1hcml6ZSh0b3RhbF9pbmp1cmllcyA9IHN1bSh0b3RhbF9pbmp1cmllcywgbmEucm0gPSBUUlVFKSkKCiMgQ3JlYXRlIHRoZSBpbmp1cmllcyB2aXN1YWxpemF0aW9uCmdncGxvdChpbmp1cmllc19ieV95ZWFyLCBhZXMoeCA9IHllYXIsIHkgPSB0b3RhbF9pbmp1cmllcykpICsKICBnZW9tX2NvbChmaWxsID0gIiM5MzcwREIiLCB3aWR0aCA9IDAuNykgKwogIGdlb21fdGV4dCgKICAgIGFlcyhsYWJlbCA9IHRvdGFsX2luanVyaWVzKSwKICAgIHZqdXN0ID0gLTAuNSwKICAgIHNpemUgPSAzLjUsCiAgICBmYW1pbHkgPSAiQXJpYWwiCiAgKSArCiAgbGFicygKICAgIHggPSAiWWVhciIsCiAgICB5ID0gTlVMTCwKICAgIHRpdGxlID0gIkluanVyaWVzIEZyb20gQ3Jhc2hlcyBBbG9uZyBEdVNhYmxlIExha2UgU2hvcmUgRHJpdmUgaW4gRGlzdHJpY3QgNiIsCiAgICBzdWJ0aXRsZSA9ICJUb3RhbCBwZXJzb25zIHN1c3RhaW5pbmcgZmF0YWwsIGluY2FwYWNpdGF0aW5nLCBub24taW5jYXBhY2l0YXRpbmcsIGFuZCBwb3NzaWJsZSBpbmp1cmllcyIsCiAgICBjYXB0aW9uID0gIkRhdGE6IENoaWNhZ28gQ2l0eSBEYXRhIFBvcnRhbCBcIlRyYWZmaWMgQ3Jhc2hlc1wiIFJldHJpZXZlZCBGZWIuIDYsIDIwMjUKSW5jbHVkZXMgY3Jhc2hlcyByZXBvcnRlZCB3aXRoaW4gMTAwZnQgb2YgRHVTYWJsZSBMYWtlIFNob3JlIERyaXZlIgogICkgKwogIHNjYWxlX3hfY29udGludW91cygKICAgIGJyZWFrcyA9IHNlcShtaW5feWVhciwgbWF4X3llYXIsIGJ5ID0gMSkKICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4xNSkpLAogICAgYnJlYWtzID0gZnVuY3Rpb24oeCkgc2VxKDAsIGNlaWxpbmcobWF4KHgpKSwgYnkgPSA1MCkKICApICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIsIG1hcmdpbiA9IG1hcmdpbihiID0gMTApKSwKICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICIjNjY2NjY2IiksCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRkZGQ0Y3IiksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0ZGRkNGNyIpLAogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiNmMGYwZjAiLCBsaW5ld2lkdGggPSAwLjMpLAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQXJpYWwiKSwKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjNjY2NjY2IiksCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiM2NjY2NjYiKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzY2NjY2NiIpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiM2NjY2NjYiKSwKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgKICAgICAgY29sb3IgPSAiIzY2NjY2NiIsCiAgICAgIHNpemUgPSA4LAogICAgICBtYXJnaW4gPSBtYXJnaW4odCA9IDEwKSwKICAgICAgaGp1c3QgPSAwCiAgICApLAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4odCA9IDE1LCByID0gMjAsIGIgPSAxNSwgbCA9IDE1LCB1bml0ID0gInB0IikKICApCmBgYAoKIyMjIEluY2FwYWNpdGF0aW5nIEluanVyaWVzIE92ZXIgVGltZQoKYGBge3IgaW5jYXAtaW5qdXJpZXMtY2hhcnQsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgUHJlcGFyZSBpbmNhcGFjaXRhdGluZyBpbmp1cmllcyBkYXRhCmluY2FwX2luanVyaWVzX2J5X3llYXIgPC0gZGlzdHJpY3Q2X2NyYXNoZXMgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIG11dGF0ZSgKICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoQ1JBU0hfREFURSksCiAgICBpbmNhcF9pbmp1cmllcyA9IElOSlVSSUVTX0lOQ0FQQUNJVEFUSU5HCiAgKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpemUodG90YWxfaW5jYXBfaW5qdXJpZXMgPSBzdW0oaW5jYXBfaW5qdXJpZXMsIG5hLnJtID0gVFJVRSkpCgojIENyZWF0ZSB0aGUgaW5jYXBhY2l0YXRpbmcgaW5qdXJpZXMgdmlzdWFsaXphdGlvbgpnZ3Bsb3QoaW5jYXBfaW5qdXJpZXNfYnlfeWVhciwgYWVzKHggPSB5ZWFyLCB5ID0gdG90YWxfaW5jYXBfaW5qdXJpZXMpKSArCiAgZ2VvbV9jb2woZmlsbCA9ICIjOTM3MERCIiwgd2lkdGggPSAwLjcpICsKICBnZW9tX3RleHQoCiAgICBhZXMobGFiZWwgPSB0b3RhbF9pbmNhcF9pbmp1cmllcyksCiAgICB2anVzdCA9IC0wLjUsCiAgICBzaXplID0gMy41LAogICAgZmFtaWx5ID0gIkFyaWFsIgogICkgKwogIGxhYnMoCiAgICB4ID0gIlllYXIiLAogICAgeSA9IE5VTEwsCiAgICB0aXRsZSA9ICJJbmNhcGFjaXRhdGluZyBJbmp1cmllcyBBbG9uZyBEdVNhYmxlIExha2UgU2hvcmUgRHJpdmUgaW4gRGlzdHJpY3QgNiIsCiAgICBzdWJ0aXRsZSA9ICJUb3RhbCBwZXJzb25zIHN1c3RhaW5pbmcgaW5jYXBhY2l0YXRpbmcvc2VyaW91cyBpbmp1cmllcyBpbiBhIGNyYXNoIiwKICAgIGNhcHRpb24gPSAiRGF0YTogQ2hpY2FnbyBDaXR5IERhdGEgUG9ydGFsIFwiVHJhZmZpYyBDcmFzaGVzXCIgUmV0cmlldmVkIEZlYi4gNiwgMjAyNQpJbmNsdWRlcyBjcmFzaGVzIHJlcG9ydGVkIHdpdGhpbiAxMDBmdCBvZiBEdVNhYmxlIExha2UgU2hvcmUgRHJpdmUiCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKAogICAgYnJlYWtzID0gc2VxKG1pbl95ZWFyLCBtYXhfeWVhciwgYnkgPSAxKQogICkgKwogIHNjYWxlX3lfY29udGludW91cygKICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjE1KSksCiAgICBicmVha3MgPSBmdW5jdGlvbih4KSBzZXEoMCwgY2VpbGluZyhtYXgoeCkpLCBieSA9IDUpCiAgKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEyLCBtYXJnaW4gPSBtYXJnaW4oYiA9IDEwKSksCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3IgPSAiIzY2NjY2NiIpLAogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0ZGRkNGNyIpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNGRkZDRjciKSwKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjZjBmMGYwIiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFyaWFsIiksCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzY2NjY2NiIpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjNjY2NjY2IiksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiM2NjY2NjYiKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjNjY2NjY2IiksCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoCiAgICAgIGNvbG9yID0gIiM2NjY2NjYiLAogICAgICBzaXplID0gOCwKICAgICAgbWFyZ2luID0gbWFyZ2luKHQgPSAxMCksCiAgICAgIGhqdXN0ID0gMAogICAgKSwKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKHQgPSAxNSwgciA9IDIwLCBiID0gMTUsIGwgPSAxNSwgdW5pdCA9ICJwdCIpCiAgKQpgYGAKCiMjIFN1bW1hcnkgU3RhdGlzdGljcwoKYGBge3IgY3Jhc2gtc3RhdHN9CiMgQ2FsY3VsYXRlIExha2UgU2hvcmUgRHJpdmUgc3RhdGlzdGljcwpsYWtlc2hvcmVfdG90YWxfY3Jhc2hlcyA8LSBucm93KGRpc3RyaWN0Nl9jcmFzaGVzKQpsYWtlc2hvcmVfdG90YWxfaW5qdXJpZXMgPC0gc3VtKGRpc3RyaWN0Nl9jcmFzaGVzJElOSlVSSUVTX1RPVEFMLCBuYS5ybSA9IFRSVUUpCmxha2VzaG9yZV90b3RhbF9pbmNhcF9pbmp1cmllcyA8LSBzdW0oZGlzdHJpY3Q2X2NyYXNoZXMkSU5KVVJJRVNfSU5DQVBBQ0lUQVRJTkcsIG5hLnJtID0gVFJVRSkKbGFrZXNob3JlX3RvdGFsX2ZhdGFsaXRpZXMgPC0gc3VtKGRpc3RyaWN0Nl9jcmFzaGVzJGZhdGFsaXR5X2NvdW50LCBuYS5ybSA9IFRSVUUpCgojIENhbGN1bGF0ZSBEaXN0cmljdCA2IHRvdGFsIHN0YXRpc3RpY3MKZGlzdHJpY3RfdG90YWxfY3Jhc2hlcyA8LSBucm93KGNyYXNoZXNfd2l0aF9kaXN0cmljdCkKZGlzdHJpY3RfdG90YWxfaW5qdXJpZXMgPC0gc3VtKGNyYXNoZXNfd2l0aF9kaXN0cmljdCRJTkpVUklFU19UT1RBTCwgbmEucm0gPSBUUlVFKQpkaXN0cmljdF90b3RhbF9pbmNhcF9pbmp1cmllcyA8LSBzdW0oY3Jhc2hlc193aXRoX2Rpc3RyaWN0JElOSlVSSUVTX0lOQ0FQQUNJVEFUSU5HLCBuYS5ybSA9IFRSVUUpCmRpc3RyaWN0X3RvdGFsX2ZhdGFsaXRpZXMgPC0gc3VtKCFpcy5uYShjcmFzaGVzX3dpdGhfZGlzdHJpY3QkRkFUQUxJVFlfVklDVElNKSAmIGNyYXNoZXNfd2l0aF9kaXN0cmljdCRGQVRBTElUWV9WSUNUSU0gIT0gIiIsIG5hLnJtID0gVFJVRSkKCiMgQ3JlYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBkYXRhZnJhbWUKY3Jhc2hfc3RhdHMgPC0gdGliYmxlKAogIE1ldHJpYyA9IGMoIlRvdGFsIENyYXNoZXMiLCAiVG90YWwgSW5qdXJpZXMiLCAiSW5jYXBhY2l0YXRpbmcgSW5qdXJpZXMiLCAiRmF0YWxpdGllcyIpLAogIGBMYWtlIFNob3JlIERyaXZlYCA9IGMobGFrZXNob3JlX3RvdGFsX2NyYXNoZXMsIGxha2VzaG9yZV90b3RhbF9pbmp1cmllcywgbGFrZXNob3JlX3RvdGFsX2luY2FwX2luanVyaWVzLCBsYWtlc2hvcmVfdG90YWxfZmF0YWxpdGllcyksCiAgYERpc3RyaWN0IDYgVG90YWxgID0gYyhkaXN0cmljdF90b3RhbF9jcmFzaGVzLCBkaXN0cmljdF90b3RhbF9pbmp1cmllcywgZGlzdHJpY3RfdG90YWxfaW5jYXBfaW5qdXJpZXMsIGRpc3RyaWN0X3RvdGFsX2ZhdGFsaXRpZXMpCikgJT4lCiAgbXV0YXRlKAogICAgYFBlcmNlbnRhZ2Ugb2YgRGlzdHJpY3QgVG90YWxgID0gc3ByaW50ZigiJS4xZiUlIiwgKGBMYWtlIFNob3JlIERyaXZlYCAvIGBEaXN0cmljdCA2IFRvdGFsYCkgKiAxMDApCiAgKQoKIyBDcmVhdGUgZm9ybWF0dGVkIHRhYmxlCmtuaXRyOjprYWJsZShjcmFzaF9zdGF0cywgZm9ybWF0ID0gImh0bWwiKSAlPiUKICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJsZWZ0IgogICkgJT4lCiAga2FibGVFeHRyYTo6cm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGNvbG9yID0gIndoaXRlIiwgYmFja2dyb3VuZCA9ICIjNEIwMDgyIikgJT4lCiAga2FibGVFeHRyYTo6Y29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpCmBgYAoKIyMgSW50ZXJhY3RpdmUgTWFwIFZpc3VhbGl6YXRpb24KCiMjIyBEaXN0cmljdCA2IEZhdGFsaXR5IE1hcAoKQ3JlYXRlIGFuIGludGVyYWN0aXZlIG1hcCBzaG93aW5nIGZhdGFsaXR5IGxvY2F0aW9ucyBpbiBEaXN0cmljdCA2OgoKYGBge3IgY3JlYXRlLW1hcCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBUcmFuc2Zvcm0gZ2VvbWV0cmllcyB0byBXR1M4NCBmb3IgbWFwcGluZwpsYWtlc2hvcmVfZmF0YWxpdGllcyA8LSBzdF90cmFuc2Zvcm0obGFrZXNob3JlX2ZhdGFsaXRpZXMsIDQzMjYpCmxha2Vfc2hvcmVfc3RyZWV0cyA8LSBzdF90cmFuc2Zvcm0obGFrZV9zaG9yZV9zdHJlZXRzLCA0MzI2KQpidWZmZXIgPC0gc3RfdHJhbnNmb3JtKGJ1ZmZlciwgNDMyNikKZGlzdHJpY3RfNiA8LSBzdF90cmFuc2Zvcm0oZGlzdHJpY3RfNiwgNDMyNikKCiMgQ3JlYXRlIExha2UgU2hvcmUgRHJpdmUgZGlzdHJpY3QgNiBmYXRhbGl0eSBtYXAKZGlzdHJpY3Q2X2ZhdGFsaXR5X21hcCA8LSBsZWFmbGV0KCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lCiAgIyBBZGQgZGlzdHJpY3QgNgogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IGRpc3RyaWN0XzYsCiAgICBmaWxsQ29sb3IgPSAiIzkzNzBEQiIsICAjIE1lZGl1bSBwdXJwbGUKICAgIGZpbGxPcGFjaXR5ID0gMC4zLAogICAgY29sb3IgPSAiIzRCMDA4MiIsICAjIEluZGlnbwogICAgd2VpZ2h0ID0gMiwKICAgIGxhYmVsID0gfnBhc3RlKCJTZW5hdGUgRGlzdHJpY3QiLCBzZW5hdGVfZGlzdHJpY3QpCiAgKSAlPiUKICAjIEFkZCBMYWtlIFNob3JlIERyaXZlCiAgYWRkUG9seWxpbmVzKAogICAgZGF0YSA9IGxha2Vfc2hvcmVfc3RyZWV0cywKICAgIGNvbG9yID0gIiNDQ0NDQ0MiLAogICAgd2VpZ2h0ID0gMwogICkgJT4lCiAgIyBBZGQgdGhlIGJ1ZmZlciBhcmVhCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gYnVmZmVyLAogICAgZmlsbENvbG9yID0gIiNDQ0NDQ0MiLCAgIyBMaWdodCBncmF5CiAgICBmaWxsT3BhY2l0eSA9IDAuMiwKICAgIGNvbG9yID0gIiNDQ0NDQ0MiLCAgIyBMaWdodCBncmF5CiAgICB3ZWlnaHQgPSAyCiAgKSAlPiUKICAjIEFkZCBjbHVzdGVyZWQgZmF0YWxpdHkgY2lyY2xlcwogIGFkZENpcmNsZU1hcmtlcnMoCiAgICBkYXRhID0gbGFrZXNob3JlX2ZhdGFsaXRpZXMsCiAgICBsbmcgPSB+c3RfY29vcmRpbmF0ZXMoZ2VvbWV0cnkpWywgMV0sCiAgICBsYXQgPSB+c3RfY29vcmRpbmF0ZXMoZ2VvbWV0cnkpWywgMl0sCiAgICByYWRpdXMgPSA2LCAgIyBCYXNlIHNpemUgZm9yIGluZGl2aWR1YWwgcG9pbnRzCiAgICBjb2xvciA9ICJyZWQiLAogICAgZmlsbENvbG9yID0gInJlZCIsCiAgICBmaWxsT3BhY2l0eSA9IDAuNiwKICAgIHN0cm9rZSA9IFRSVUUsCiAgICB3ZWlnaHQgPSAxLAogICAgcG9wdXAgPSB+cGFzdGUoCiAgICAgICJEYXRlOiIsIENSQVNIX0RBVEUsICI8YnI+IiwKICAgICAgIkZhdGFsaXRpZXM6IiwgZmF0YWxpdHlfY291bnQsICI8YnI+IiwKICAgICAgIlR5cGU6IiwgRklSU1RfQ1JBU0hfVFlQRQogICAgKSwKICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoCiAgICAgIG1heENsdXN0ZXJSYWRpdXMgPSA1MCAqIDAuMDAwMywgICMgQXBwcm94aW1hdGUgY29udmVyc2lvbiBvZiA1MGZ0IHRvIGRlZ3JlZXMKICAgICAgc3BpZGVyZnlPbk1heFpvb20gPSBUUlVFLAogICAgICBpY29uQ3JlYXRlRnVuY3Rpb24gPSBKUygiCiAgICAgICAgZnVuY3Rpb24oY2x1c3RlcikgewogICAgICAgICAgdmFyIGNvdW50ID0gY2x1c3Rlci5nZXRDaGlsZENvdW50KCk7CiAgICAgICAgICB2YXIgc2l6ZSA9IE1hdGgubWluKDUwLCAyMCArIE1hdGguc3FydChjb3VudCkgKiA4KTsKICAgICAgICAgIHJldHVybiBuZXcgTC5EaXZJY29uKHsKICAgICAgICAgICAgaHRtbDogJzxkaXYgc3R5bGU9XCJiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMCwgMCwgMC42KTsgd2lkdGg6ICcgKyBzaXplICsgJ3B4OyBoZWlnaHQ6ICcgKyBzaXplICsgJ3B4OyBtYXJnaW4tbGVmdDogLScgKyAoc2l6ZS8yKSArICdweDsgbWFyZ2luLXRvcDogLScgKyAoc2l6ZS8yKSArICdweDsgYm9yZGVyLXJhZGl1czogNTAlOyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsgY29sb3I6IHdoaXRlOyBmb250LXdlaWdodDogYm9sZDsgYm9yZGVyOiAycHggc29saWQgcmdiYSgyNTUsIDAsIDAsIDAuOCk7XCI+JyArIGNvdW50ICsgJzwvZGl2PicsCiAgICAgICAgICAgIGNsYXNzTmFtZTogJ21hcmtlci1jbHVzdGVyLWN1c3RvbScsCiAgICAgICAgICAgIGljb25TaXplOiBMLnBvaW50KHNpemUsIHNpemUpCiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICIpCiAgICApCiAgKSAlPiUKICBzZXRWaWV3KGxuZyA9IC04Ny42Mjk4LCBsYXQgPSA0MS44NzgxLCB6b29tID0gMTIpCgojIERpc3BsYXkgdGhlIG1hcApkaXN0cmljdDZfZmF0YWxpdHlfbWFwCmBgYAoKIyMjIExha2UgU2hvcmUgRHJpdmUgSW5qdXJ5IE1hcAoKQ3JlYXRlIGludGVyYWN0aXZlIG1hcHMgc2hvd2luZyBpbmp1cmllcywgaW5jYXBhY2l0YXRpbmcgaW5qdXJpZXMsIGFuZCBmYXRhbGl0aWVzIGFsb25nIExha2UgU2hvcmUgRHJpdmU6CgpgYGB7ciBjcmVhdGUtbGFrZXNob3JlLW1hcHMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgVHJhbnNmb3JtIGdlb21ldHJpZXMgdG8gV0dTODQgZm9yIG1hcHBpbmcKbGFrZXNob3JlX2NyYXNoZXNfNDMyNiA8LSBzdF90cmFuc2Zvcm0obGFrZXNob3JlX2NyYXNoZXMsIDQzMjYpCmxha2Vmcm9udF9kaXN0cmljdHNfNDMyNiA8LSBzdF90cmFuc2Zvcm0obGFrZWZyb250X2Rpc3RyaWN0cywgNDMyNikKCiMgQ3JlYXRlIGJpbmFyeSBpbmRpY2F0b3JzIGZvciBjcmFzaGVzIHdpdGggaW5qdXJpZXMKbGFrZXNob3JlX2NyYXNoZXNfNDMyNiA8LSBsYWtlc2hvcmVfY3Jhc2hlc180MzI2ICU+JQogIG11dGF0ZSgKICAgIGhhc19pbmp1cmllcyA9IGlmZWxzZShJTkpVUklFU19UT1RBTCA+IDAsIDEsIDApLAogICAgaGFzX2luY2FwX2luanVyaWVzID0gaWZlbHNlKElOSlVSSUVTX0lOQ0FQQUNJVEFUSU5HID4gMCwgMSwgMCkKICApCgojIENyZWF0ZSBpbmp1cmllcyBoZWF0bWFwIHdpdGggYWRqdXN0ZWQgcGFyYW1ldGVycwppbmp1cmllc19tYXAgPC0gbGVhZmxldCgpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pICU+JQogICMgQWRkIGxha2Vmcm9udCBkaXN0cmljdHMgb3V0bGluZXMKICBhZGRQb2x5Z29ucygKICAgIGRhdGEgPSBsYWtlZnJvbnRfZGlzdHJpY3RzXzQzMjYsCiAgICBmaWxsT3BhY2l0eSA9IDAsCiAgICBjb2xvciA9ICIjNEIwMDgyIiwgICMgSW5kaWdvCiAgICB3ZWlnaHQgPSAyLAogICAgbGFiZWwgPSB+cGFzdGUoIlNlbmF0ZSBEaXN0cmljdCIsIHNlbmF0ZV9kaXN0cmljdCkKICApICU+JQogICMgQWRkIExha2UgU2hvcmUgRHJpdmUKICBhZGRQb2x5bGluZXMoCiAgICBkYXRhID0gbGFrZV9zaG9yZV9zdHJlZXRzLAogICAgY29sb3IgPSAiIzY2NjY2NiIsCiAgICB3ZWlnaHQgPSAzCiAgKSAlPiUKICAjIEFkZCBpbmp1cmllcyBoZWF0bWFwCiAgYWRkSGVhdG1hcCgKICAgIGRhdGEgPSBsYWtlc2hvcmVfY3Jhc2hlc180MzI2ICU+JSBmaWx0ZXIoaGFzX2luanVyaWVzID09IDEpLAogICAgbG5nID0gfnN0X2Nvb3JkaW5hdGVzKGdlb21ldHJ5KVssIDFdLAogICAgbGF0ID0gfnN0X2Nvb3JkaW5hdGVzKGdlb21ldHJ5KVssIDJdLAogICAgaW50ZW5zaXR5ID0gfjEsICAjIEVhY2ggY3Jhc2ggd2l0aCBpbmp1cmllcyBjb3VudHMgYXMgMQogICAgYmx1ciA9IDE1LAogICAgbWF4ID0gMC4xLAogICAgcmFkaXVzID0gMTIKICApICU+JQogIHNldFZpZXcobG5nID0gLTg3LjYyOTgsIGxhdCA9IDQxLjg3ODEsIHpvb20gPSAxMikKCiMgRGlzcGxheSB0aGUgbWFwCmluanVyaWVzX21hcApgYGAKCiMjIyBMYWtlIFNob3JlIERyaXZlIEluY2FwYWNpdGF0aW5nIEluanVyeSBNYXAKCmBgYHtyIGluY2FwLWluanVyaWVzLW1hcCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgQ3JlYXRlIGluY2FwYWNpdGF0aW5nIGluanVyaWVzIGhlYXRtYXAKaW5jYXBfaW5qdXJpZXNfbWFwIDwtIGxlYWZsZXQoKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uKSAlPiUKICAjIEFkZCBsYWtlZnJvbnQgZGlzdHJpY3RzIG91dGxpbmVzCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gbGFrZWZyb250X2Rpc3RyaWN0c180MzI2LAogICAgZmlsbE9wYWNpdHkgPSAwLAogICAgY29sb3IgPSAiIzRCMDA4MiIsICAjIEluZGlnbwogICAgd2VpZ2h0ID0gMiwKICAgIGxhYmVsID0gfnBhc3RlKCJTZW5hdGUgRGlzdHJpY3QiLCBzZW5hdGVfZGlzdHJpY3QpCiAgKSAlPiUKICAjIEFkZCBMYWtlIFNob3JlIERyaXZlCiAgYWRkUG9seWxpbmVzKAogICAgZGF0YSA9IGxha2Vfc2hvcmVfc3RyZWV0cywKICAgIGNvbG9yID0gIiM2NjY2NjYiLAogICAgd2VpZ2h0ID0gMwogICkgJT4lCiAgIyBBZGQgaW5jYXBhY2l0YXRpbmcgaW5qdXJpZXMgaGVhdG1hcAogIGFkZEhlYXRtYXAoCiAgICBkYXRhID0gbGFrZXNob3JlX2NyYXNoZXNfNDMyNiAlPiUgZmlsdGVyKGhhc19pbmNhcF9pbmp1cmllcyA9PSAxKSwKICAgIGxuZyA9IH5zdF9jb29yZGluYXRlcyhnZW9tZXRyeSlbLCAxXSwKICAgIGxhdCA9IH5zdF9jb29yZGluYXRlcyhnZW9tZXRyeSlbLCAyXSwKICAgIGludGVuc2l0eSA9IH4xLCAgIyBFYWNoIGNyYXNoIHdpdGggaW5jYXBhY2l0YXRpbmcgaW5qdXJpZXMgY291bnRzIGFzIDEKICAgIGJsdXIgPSAxNSwKICAgIG1heCA9IDAuMSwKICAgIHJhZGl1cyA9IDEyCiAgKSAlPiUKICBzZXRWaWV3KGxuZyA9IC04Ny42Mjk4LCBsYXQgPSA0MS44NzgxLCB6b29tID0gMTIpCgojIERpc3BsYXkgdGhlIG1hcAppbmNhcF9pbmp1cmllc19tYXAKCiMjIyBMYWtlIFNob3JlIERyaXZlIEZhdGFsaXR5IE1hcAoKIyBDcmVhdGUgZmF0YWxpdGllcyBtYXAgd2l0aCBjbHVzdGVycwpmYXRhbGl0aWVzX21hcCA8LSBsZWFmbGV0KCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lCiAgIyBBZGQgbGFrZWZyb250IGRpc3RyaWN0cyBvdXRsaW5lcwogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IGxha2Vmcm9udF9kaXN0cmljdHNfNDMyNiwKICAgIGZpbGxPcGFjaXR5ID0gMCwKICAgIGNvbG9yID0gIiM0QjAwODIiLCAgIyBJbmRpZ28KICAgIHdlaWdodCA9IDIsCiAgICBsYWJlbCA9IH5wYXN0ZSgiU2VuYXRlIERpc3RyaWN0Iiwgc2VuYXRlX2Rpc3RyaWN0KQogICkgJT4lCiAgIyBBZGQgTGFrZSBTaG9yZSBEcml2ZQogIGFkZFBvbHlsaW5lcygKICAgIGRhdGEgPSBsYWtlX3Nob3JlX3N0cmVldHMsCiAgICBjb2xvciA9ICIjNjY2NjY2IiwKICAgIHdlaWdodCA9IDMKICApICU+JQogICMgQWRkIGNsdXN0ZXJlZCBmYXRhbGl0eSBjaXJjbGVzCiAgYWRkQ2lyY2xlTWFya2VycygKICAgIGRhdGEgPSBsYWtlc2hvcmVfZmF0YWxpdGllcywKICAgIGxuZyA9IH5zdF9jb29yZGluYXRlcyhnZW9tZXRyeSlbLCAxXSwKICAgIGxhdCA9IH5zdF9jb29yZGluYXRlcyhnZW9tZXRyeSlbLCAyXSwKICAgIHJhZGl1cyA9IDYsCiAgICBjb2xvciA9ICJyZWQiLAogICAgZmlsbENvbG9yID0gInJlZCIsCiAgICBmaWxsT3BhY2l0eSA9IDAuNiwKICAgIHN0cm9rZSA9IFRSVUUsCiAgICB3ZWlnaHQgPSAxLAogICAgcG9wdXAgPSB+cGFzdGUoCiAgICAgICJEYXRlOiIsIENSQVNIX0RBVEUsICI8YnI+IiwKICAgICAgIkZhdGFsaXRpZXM6IiwgZmF0YWxpdHlfY291bnQsICI8YnI+IiwKICAgICAgIlR5cGU6IiwgRklSU1RfQ1JBU0hfVFlQRQogICAgKSwKICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoCiAgICAgIG1heENsdXN0ZXJSYWRpdXMgPSA1MCAqIDAuMDAwMywKICAgICAgc3BpZGVyZnlPbk1heFpvb20gPSBUUlVFLAogICAgICBpY29uQ3JlYXRlRnVuY3Rpb24gPSBKUygiCiAgICAgICAgZnVuY3Rpb24oY2x1c3RlcikgewogICAgICAgICAgdmFyIGNvdW50ID0gY2x1c3Rlci5nZXRDaGlsZENvdW50KCk7CiAgICAgICAgICB2YXIgc2l6ZSA9IE1hdGgubWluKDUwLCAyMCArIE1hdGguc3FydChjb3VudCkgKiA4KTsKICAgICAgICAgIHJldHVybiBuZXcgTC5EaXZJY29uKHsKICAgICAgICAgICAgaHRtbDogJzxkaXYgc3R5bGU9XCJiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMCwgMCwgMC42KTsgd2lkdGg6ICcgKyBzaXplICsgJ3B4OyBoZWlnaHQ6ICcgKyBzaXplICsgJ3B4OyBtYXJnaW4tbGVmdDogLScgKyAoc2l6ZS8yKSArICdweDsgbWFyZ2luLXRvcDogLScgKyAoc2l6ZS8yKSArICdweDsgYm9yZGVyLXJhZGl1czogNTAlOyBkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsgY29sb3I6IHdoaXRlOyBmb250LXdlaWdodDogYm9sZDsgYm9yZGVyOiAycHggc29saWQgcmdiYSgyNTUsIDAsIDAsIDAuOCk7XCI+JyArIGNvdW50ICsgJzwvZGl2PicsCiAgICAgICAgICAgIGNsYXNzTmFtZTogJ21hcmtlci1jbHVzdGVyLWN1c3RvbScsCiAgICAgICAgICAgIGljb25TaXplOiBMLnBvaW50KHNpemUsIHNpemUpCiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgICIpCiAgICApCiAgKSAlPiUKICBzZXRWaWV3KGxuZyA9IC04Ny42Mjk4LCBsYXQgPSA0MS44NzgxLCB6b29tID0gMTIpCgojIERpc3BsYXkgdGhlIG1hcApmYXRhbGl0aWVzX21hcApgYGAK